<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>手写Promise</title>
</head>

<body>
    <h2>构造函数</h2>
    <script>

        function resolvePromise(p2, x, resolve, reject) {
            if (x === p2) {
                console.log('我被调用了')
                throw new TypeError('Chaining cycle detected for promise #<Promise>')
            }
            if (x instanceof HMPPromise) {
                x.then(res => resolve(res), err => reject(err))
            } else {
                resolve(x)
            }
        }

        function runAsynctask(callback) {
            if (typeof queueMicrotask === 'function') {
                queueMicrotask(callback)
            }
            else if (typeof MutationObserver === 'function') {
                const obs = new MutationObserver(callback)
                const divnode = document.createElement('div')
                obs.observe(divnode, { childList: true })
                divnode.innerHTML = '123'
            }
            else {
                setTimeout(() => {
                    callback()
                })
            }
        }

        const PENDING = 'pending'
        const FULFILLED = 'fulfilled'
        const REJECTED = 'rejected'
        class HMPPromise {
            #handlers = []
            state = PENDING
            result = undefined
            constructor(func) {
                const resolve = (result) => {
                    if (this.state === PENDING) {
                        this.state = FULFILLED
                        this.result = result

                        this.#handlers.forEach(({ onFulifilled }) => {
                            onFulifilled(this.result)
                        })
                    }
                }
                const reject = (result) => {
                    if (this.state === PENDING) {
                        this.state = REJECTED
                        this.result = result
                        this.#handlers.forEach(({ onRejected }) => {
                            onRejected(this.result)
                        })
                    }

                }
                try { func(resolve, reject) } catch (error) {
                    reject(error)
                }
            }

            then(onFulifilled, onRejected) {
                onFulifilled = typeof onFulifilled === 'function' ? onFulifilled : x => x
                onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x }

                const p2 = new HMPPromise((resolve, reject) => {
                    if (this.state === FULFILLED) {
                        runAsynctask(() => {
                            try {
                                console.log('载做一个onfullid的。then执行')
                                const x = onFulifilled(this.result)
                                resolvePromise(p2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }

                        })
                    }
                    else if (this.state === REJECTED) {
                        try {
                            // console.log('载做一个onfullid的。then执行')
                            const x = onRejected(this.result)
                            resolvePromise(p2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }
                    else if (this.state === PENDING) {
                        this.#handlers.push({
                            onFulifilled: () => {
                                runAsynctask(() => {
                                    console.log('先执行runAsynctask回调函数')
                                    onFulifilled(this.result)
                                })
                            },
                            onRejected: () => {
                                runAsynctask(() => {
                                    onRejected(this.result)
                                }
                                )
                            }
                        })
                    }
                })
                return p2
            }

            catch(onRejected) {
                return this.then(undefined, onRejected)
            }

            finally(onFinally){
                return this.then(onFinally,onFinally)
            }

            static resolve(value)
            {
                if(value instanceof HMPPromise)
                {
                    return value
                }
                return new HMPPromise((resolve)=>{
                    resolve(value)
                })
            }

            static reject(value){
                return new HMPPromise((undefined,reject)=>{
                    reject(value)
                })
            }

            static race(promises)
            {
                return new HMPPromise((resolve,reject)=>{
                    if(!Array.isArray(promises))
                    {
                        return reject(new TypeError('Argument is not iterable'))
                    }

                    promises.forEach(p=>{
                        HMPPromise.resolve(p).then(res=>{resolve(res)},err=>{reject(err)})
                    })
                })
            }

            static all(peomises){
                return new HMPPromise((resolve,reject)=>{
                    if(!Array.isArray(peomises))
                    {
                        return reject(new TypeError('Argument is not iterable'))
                    }

                    peomises.length === 0&&resolve(promises)

                    const results = []
                    const count = 0
                    promises.forEach((p,index)=>{
                        HMPPromise.resolve(p).then(
                            res=>{
                                results[index]=res
                                count++
                                count === promises.length&&resolve(results)
                            },
                            err=>{
                                reject(err)
                            }
                        )
                    })
                })
            }


            static allSettled(peomises){
                console.log('执行了')
                return new Promise((resolve,reject)=>{
                    if(!Array.isArray(peomises))
                    {
                        return reject(new TypeError('Argument is not iterable'))
                    }

                    peomises.length===0&&resolve(peomises)
                    
                    const results= []
                    let count =0
                    peomises.forEach((p,index)=>{
                        HMPPromise.resolve(p).then(
                            res=>{
                                results[index] = {status:FULFILLED,value:res}
                                count++
                                count === peomises.length && resolve(results)
                            },
                            err=>{
                                results[index] = {status:REJECTED,value:err}
                                count++
                                count === peomises.length && resolve(results)
                            }
                        )
                    })
                })
            }


            static any(peomises){
                return new HMPPromise((resolve,reject)=>{
                    if(!Array.isArray(peomises))
                    {
                        return reject(new TypeError('Argument is not iterable'))
                    }
                    peomises.length === 0 && reject(new AggregateError(peomises, 'All promises were rejected'))

                    const results=[]
                    let count = 0
                    peomises.forEach((p,index)=>{
                        HMPPromise.resolve(p).then(
                            res=>{
                                resolve(res)
                            },
                            errors=>{
                                results[index]=errors
                                count++
                                count===peomises.length&&reject(new AggregateError(results, 'All promises were rejected'))
                            }
                        )
                    })
                })
            }
        }
        // console.log('top')
        // const p = new HMPPromise((resolve, reject) => {
        //     resolve(1)
        //     // setTimeout(()=>{
        //     //     resolve(1)
        //     // },2000)
        // })
        // const p2 = p.then(res => {
        //     console.log('p1:', res)
        //     // throw 'throw-error'
        //     // return p2
        //     // console.log('p:',p)
        //     return res
        // })
        // p2.then(res => {
        //     console.log('p2:', res)
        // }, err => {
        //     console.log('p2:', err)
        // })

            // ------------- 测试代码 手写Promise -------------
    // const p1 =  HMPPromise.resolve(1)
    // const p2 = 2
    // const p3 = new HMPPromise((resolve, reject) => {
    //   setTimeout(() => {
    //     reject(3)
    //   }, 1000)
    // })
    // HMPPromise.allSettled([p1, p2]).then(res => {
    //   // HMPromise.allSettled().then(res => {
    //   // HMPromise.allSettled([]).then(res => {
    //   console.log('res:', res)
    // }, err => {
    //   console.log('err:', err)
    // })

            // ------------- 测试代码 手写Promise -------------
    const p1 = new HMPPromise((resolve, reject) => {
      setTimeout(() => {
        reject(1)
      }, 2000)
    })
    // const p2 = 2
    const p2 = HMPPromise.reject(2)
    const p3 = new HMPPromise((resolve, reject) => {
      setTimeout(() => {
        // resolve(3)
        reject(3)
      }, 1000)
    })

    HMPPromise.any([p1, p2, p3]).then(res => {
      // HMPromise.any().then(res => {
      // HMPromise.any([]).then(res => {
      console.log('res:', res)
    }, err => {
      console.dir(err)
    })

    </script>
</body>

</html>